import requests
import time
import re
from datetime import datetime
from typing import Optional, Dict, Any, List
from dataclasses import dataclass


@dataclass
class PhoneOrder:
    id: str
    phone: str
    country: str
    operator: str
    product: str
    price: float
    status: str
    
    @classmethod
    def from_dict(cls, data: dict):
        return cls(
            id=str(data.get('id', '')),
            phone=data.get('phone', ''),
            country=data.get('country', ''),
            operator=data.get('operator', ''),
            product=data.get('product', ''),
            price=data.get('price', 0.0),
            status=data.get('status', '')
        )


@dataclass
class SMS:
    id: str
    text: str
    sender: str
    received_at: str
    code: Optional[str] = None
    
    @classmethod
    def from_dict(cls, data: dict):
        sms = cls(
            id=str(data.get('ID', data.get('id', ''))),
            text=data.get('text', ''),
            sender=data.get('sender', ''),
            received_at=data.get('created_at', data.get('date', '')))
        sms.code = cls.extract_code(sms.text)
        return sms
    
    @staticmethod
    def extract_code(text: str) -> Optional[str]:
        patterns = [
            r'(?:code|OTP|PIN)[:\s]+(\d{4,8})',
            r'verification[:\s]+(\d{4,8})',
            r'is[:\s]+(\d{4,8})',
            r':\s*(\d{6})',
            r'\b(\d{6})\b',
            r'\b(\d{4,8})\b',
        ]
        
        for pattern in patterns:
            match = re.search(pattern, text, re.IGNORECASE)
            if match:
                return match.group(1)
        return None


class PhoneVerifier:
    
    def __init__(self, token: str, xsrf_token: Optional[str] = None):
        self.token = token
        self.xsrf_token = xsrf_token
        self.base_url = "https://5sim.net/v1"
        self.session = requests.Session()
        self._setup_session()
    
    def _setup_session(self):
        self.session.headers.update({
            'Authorization': f'Bearer {self.token}',
            'Accept': 'application/json'
        })
        
        if self.xsrf_token:
            self.session.headers['X-XSRF-TOKEN'] = self.xsrf_token
    
    def buy_number(
        self,
        country: str = "england",
        operator: str = "virtual60",
        product: str = "other",
        forwarding: Optional[bool] = None,
        number: Optional[str] = None,
        reuse: Optional[bool] = None,
        voice: Optional[bool] = None,
        ref: Optional[str] = None
    ) -> Optional[PhoneOrder]:
        url = f"{self.base_url}/user/buy/activation/{country}/{operator}/{product}"
        
        params = {}
        if forwarding is not None:
            params['forwarding'] = str(forwarding).lower()
        if number:
            params['number'] = number
        if reuse is not None:
            params['reuse'] = str(reuse).lower()
        if voice is not None:
            params['voice'] = str(voice).lower()
        if ref:
            params['ref'] = ref
        
        try:
            response = self.session.get(url, params=params)
            response.raise_for_status()
            data = response.json()
            print(f"Phone purchase response: {data}")
            return PhoneOrder.from_dict(data)
        except requests.exceptions.RequestException as e:
            print(f"Error purchasing number: {e}")
            if hasattr(e, 'response') and e.response is not None:
                print(f"Response status: {e.response.status_code}")
                print(f"Response content: {e.response.text}")
            return None
    
    def get_sms_inbox(self, order_id: str) -> List[SMS]:
        url = f"{self.base_url}/user/sms/inbox/{order_id}"
        
        try:
            response = self.session.get(url)
            response.raise_for_status()
            data = response.json()
            
            print(f"SMS API Response for order {order_id}: {data}")
            
            sms_list = []
            sms_data_list = data.get('Data')
            if sms_data_list is not None:
                for sms_data in sms_data_list:
                    sms_list.append(SMS.from_dict(sms_data))
            
            if sms_list:
                print(f"Found {len(sms_list)} SMS messages")
            
            return sms_list
        except requests.exceptions.RequestException as e:
            print(f"Error fetching SMS inbox: {e}")
            if hasattr(e, 'response') and e.response is not None:
                print(f"Response status: {e.response.status_code}")
                print(f"Response content: {e.response.text}")
            return []
    
    def check_sms_status(self, order_id: str) -> Optional[Dict[str, Any]]:
        url = f"{self.base_url}/user/check/{order_id}"
        
        try:
            response = self.session.get(url)
            response.raise_for_status()
            data = response.json()
            print(f"SMS status check for order {order_id}: {data}")
            return data
        except requests.exceptions.RequestException as e:
            print(f"Error checking SMS status: {e}")
            if hasattr(e, 'response') and e.response is not None:
                print(f"Response status: {e.response.status_code}")
                print(f"Response content: {e.response.text}")
            return None
    
    def wait_for_sms(
        self,
        order_id: str,
        max_wait: int = 300,
        check_interval: int = 5,
        target_sender: Optional[str] = None
    ) -> Optional[SMS]:
        start_time = time.time()
        checked_ids = set()
        
        while time.time() - start_time < max_wait:
            
            sms_list = self.get_sms_inbox(order_id)
            if sms_list:
                for sms in sms_list:
                    if sms.id in checked_ids:
                        continue
                    
                    checked_ids.add(sms.id)
                    print(f"SMS from {sms.sender}: {sms.text}")
                    
                    if sms.code:
                        print(f"✓ Found SMS with code: {sms.code}")
                        return sms
            
            status = self.check_sms_status(order_id)
            if status:
                sms_data = status.get('sms')
                
                if isinstance(sms_data, list) and sms_data:
                    for sms_item in sms_data:
                        if isinstance(sms_item, dict):
                            sms_text = sms_item.get('text', '')
                            sms_code = sms_item.get('code', '')
                            sms_id = f"{order_id}_{sms_item.get('created_at', '')}"
                            
                            if sms_id in checked_ids:
                                continue
                                
                            checked_ids.add(sms_id)
                            print(f"SMS via status check: {sms_text}")
                            
                            if not sms_code:
                                sms_code = SMS.extract_code(sms_text)
                            
                            if sms_code:
                                print(f"✓ Found SMS with code: {sms_code}")
                                sms = SMS(
                                    id=sms_id,
                                    text=sms_text,
                                    sender=sms_item.get('sender', 'unknown'),
                                    received_at=sms_item.get('created_at', datetime.now().isoformat()),
                                    code=sms_code
                                )
                                return sms
                
                elif isinstance(sms_data, str) and sms_data:
                    if sms_data not in checked_ids:
                        checked_ids.add(sms_data)
                        print(f"SMS via status check: {sms_data}")
                        
                        code = SMS.extract_code(sms_data)
                        if code:
                            print(f"✓ Found SMS with code: {code}")
                            sms = SMS(
                                id=str(order_id),
                                text=sms_data,
                                sender="unknown",
                                received_at=datetime.now().isoformat(),
                                code=code
                            )
                            return sms
                
                order_status = status.get('status')
                if order_status in ['CANCELED', 'BANNED']:
                    print(f"Order {order_id} status: {order_status}")
                    return None
            
            time.sleep(check_interval)
        
        print(f"Timeout waiting for SMS after {max_wait} seconds")
        return None
    
    def get_balance(self) -> Optional[float]:
        url = f"{self.base_url}/user/profile"
        
        try:
            response = self.session.get(url)
            response.raise_for_status()
            data = response.json()
            return data.get('balance', 0.0)
        except requests.exceptions.RequestException as e:
            print(f"Error fetching balance: {e}")
            return None
    
    def cancel_order(self, order_id: str) -> bool:
        url = f"{self.base_url}/user/cancel/{order_id}"
        
        try:
            response = self.session.get(url)
            response.raise_for_status()
            data = response.json()
            print(f"✓ Order {order_id} canceled successfully")
            if 'phone' in data:
                print(f"  Phone: {data['phone']}")
            if 'status' in data:
                print(f"  Status: {data['status']}")
            return True
        except requests.exceptions.RequestException as e:
            print(f"Error canceling order {order_id}: {e}")
            if hasattr(e, 'response') and e.response is not None:
                print(f"Response: {e.response.text}")
            return False
    
    def finish_order(self, order_id: str) -> bool:
        url = f"{self.base_url}/user/finish/{order_id}"
        
        try:
            response = self.session.get(url)
            response.raise_for_status()
            return True
        except requests.exceptions.RequestException as e:
            print(f"Error finishing order: {e}")
            return False
